虽然从技术上讲 JavaScript 是一门面向对象语言,但是缺少传统的面向对象编程语言所具备的某些基本结构,包括类和接口。因此引用类型虽然有点像类,但跟类并不是一个概念,仅仅是一个把数据和功能组织到一起的结构。
对象被认为是某个特定引用类型的实例,新对象通过使用 new 操作符后跟一个构造函数来创建
let now = new Date();
这行代码创建了引用类型 Date 的一个新实例,并将它保存在变量 now 中。Date()在这里就是构造函数,它负责创建一个只有默认属性和方法的简单对象.
要创建日期对象,就使用 new 操作符来调用 Date 构造函数:
let now = new Date();
在不给 Date 构造函数传参数的情况下,创建的对象将保存当前日期和时间。要基于其他日期和时间创建日期对象,必须传入其毫秒表示。
Date还有两个辅助方法:Date.parse() 和 Date.UTC() 来获取毫秒数
Date.parse()方法接收一个表示日期的字符串参数,尝试将这个字符串转换为表示该日期的毫秒数。目前支持以下日期格式:
let someDate = new Date(Date.parse("May 23, 2019"));
// Date构造函数会隐式调用 Date.parse 方法
let someDate = new Date("May 23, 2019");
Date.UTC()方法通过参数的形式接收目标日期。传给 Date.UTC() 的参数是年、零起点月数(1 月是 0,2 月是 1,以此类推)、日(1~31)、时(0~23)、 分、秒和毫秒。这些参数中,只有年和月是必需的。如果不提供日,那么默认为 1 日,其他参数的默认值都是 0。
// GMT时间2000年1月1日零点
let y2k = new Date(Date.UTC(2000, 0));
// GMT时间2005年5月5日下午5点55分55秒
let allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));
// Date构造函数会隐式调用 Date.UTC 方法
// 但是这样创建的是本地日期,不是 GMT 日期
// 本地时间2000年1月1日零点
let y2k = new Date(2000, 0);
// 本地时间2005年5月5日下午5点55分55秒
let allFives = new Date(2005, 4, 5, 17, 55, 55);
还可以通过 Date.now() 方法获取代码执行时的毫秒数,一般用于计算时间差或网络请求校验
// 起始时间
let start = Date.now();
// 调用函数
doSomething();
// 结束时间
let stop = Date.now(), result = stop - start;
// 计算函数运行所消耗的时间
Date类型提供了一些默认的格式化日期的方法:
这些方法会因浏览器而异,因此不能用于在用户界面上一致地显示日期,如果需要单独提取日期中的特定部分,则需要使用其他API(详见原文列表)
JS通过 RegExp 类型进行正则匹配,每个正则表达式可以带0~n个 flages ,用于控制正则表达式的行为
let expression = /pattern/flags;
使用不同模式和标记可以创建出各种正则表达式,例如:
// 匹配字符串中的所有"at"
let pattern1 = /at/g;
// 匹配第一个"bat"或"cat",忽略大小写
let pattern2 = /[bc]at/i;
// 匹配所有以"at"结尾的三字符组合,忽略大小写
let pattern3 = /.at/gi;
所有元字符在模式中也必须转义,包括:( [ { \ ^ $ | ) ] } ? * + .
元字符在正则表达式中都有一种或多种特殊功能,所以要匹配上面这些字符本身,就必须使用反斜杠来转义
// 匹配第一个"bat"或"cat",忽略大小写
let pattern1 = /[bc]at/i;
// 匹配第一个"[bc]at",忽略大小写
let pattern2 = /\[bc\]at/i;
// 匹配所有以"at"结尾的三字符组合,忽略大小写
let pattern3 = /.at/gi;
// 匹配所有".at",忽略大小写
let pattern4 = /\.at/gi;
正则表达式也可以使用 RegExp 构造函数来创建,它接收两个参数:模式字符串和(可选的)标记字符串,以下两个表达式是等效的:
// 匹配第一个"bat"或"cat",忽略大小写
let pattern1 = /[bc]at/i;
// 跟 pattern1 一样,只不过是用构造函数创建的
let pattern2 = new RegExp("[bc]at", "i");
此外,使用 RegExp 也可以基于已有的正则表达式实例,并可选择性地修改它们的标记:
const re1 = /cat/g;
console.log(re1); // "/cat/g"
// 沿用之前的flag
const re2 = new RegExp(re1);
console.log(re2); // "/cat/g"
// 修改了原来的flag,从 g 改为了 i
const re3 = new RegExp(re1, "i");
console.log(re3); // "/cat/i"
每个 RegExp 实例都有下列属性,提供有关模式的各方面信息:
let pattern = /\[bc\]at/i;
console.log(pattern.global); // false
console.log(pattern.ignoreCase); // true
console.log(pattern.multiline); // false
console.log(pattern.lastIndex); // 0
console.log(pattern.source); // "\[bc\]at"
console.log(pattern.flags); // "i"
// 使用RegExp构造函数具有同样效果,可以自行实现
RegExp 实例的主要方法是 exec(),主要用于配合捕获组使用。这个方法只接收一个参数,即要应用模式的字符串。如果找到了匹配项,则返回包含第一个匹配信息的数组;如果没找到匹配项,则返回 null。返回的数组虽然是 Array 的实例,但包含两个额外的属性:index 和 input。index 是字符串中匹配模式的起始位置,input 是要查找的字符串。这个数组的第一个元素是匹配整个模式的字符串,其他元素是与表达式中的捕获组匹配的字符串。如果模式中没有捕获组,则数组只包含一个元素。
let text = "mom and dad and baby";
let pattern = /mom( and dad( and baby)?)?/gi;
let matches = pattern.exec(text);
console.log(matches.index); //0
console.log(matches.input); // "mom and dad and baby"
console.log(matches[0]); // "mom and dad and baby"
console.log(matches[1]); // " and dad and baby"
console.log(matches[2]); // " and baby"
在上面例子中,模式包含两个捕获组:最内部的匹配项" and baby",以及外部的匹配项" and dad"或" and dad and baby"。调用 exec()后找到了一个匹配项。因为整个字符串匹配模式,所以 matchs 数组的 index 属性就是 0。数组的第一个元素是匹配的整个字符串,第二个元素是匹配第一个捕获组的字符串,第三个元素是匹配第二个捕获组的字符串。
如果模式设置了全局标记,则每次调用 exec()方法会返回下一个匹配的信息。如果没有设置全局标记,则无论对同一个字符串调用多少次 exec(),也只会返回第一个匹配的信息
let text = "cat, bat, sat, fat";
let pattern = /.at/; // 没有设置全局标记
// 调用 exec()只返回第一个匹配项("cat")
let matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 0
// 第二次进行匹配
matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 0,lastIndex 在非全局模式下始终不变
let text = "cat, bat, sat, fat";
let pattern = /.at/g; // 设置了全局标记
// 每次调用 exec()都会返回字符串中的下一个匹配项
let matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 3
// 在全局匹配模式下,每次调用exec()都会更新 lastIndex 值
// 以反映上次匹配的最后一个字符的索引
matches = pattern.exec(text);
console.log(matches.index); // 5
console.log(matches[0]); // bat
console.log(pattern.lastIndex); // 8
matches = pattern.exec(text);
console.log(matches.index); // 10
console.log(matches[0]); // sat
console.log(pattern.lastIndex); // 13
如果模式设置了粘附标记 y,则每次调用 exec() 就只会在 lastIndex 的位置上寻找匹配项,并且粘附标记会覆盖全局标记。
let text = "cat, bat, sat, fat";
let pattern = /.at/y;
let matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 3
// 以索引3对应的字符开头找不到匹配项,因此exec()返回null
// exec()没找到匹配项,于是将lastIndex设置为0
matches = pattern.exec(text);
console.log(matches); // null
console.log(pattern.lastIndex); // 0
// 向前设置lastIndex可以让粘附的模式通过exec()找到下一个匹配项:
pattern.lastIndex = 5;
matches = pattern.exec(text);
console.log(matches.index); // 5
console.log(matches[0]); // bat
console.log(pattern.lastIndex); // 8
正则表达式的另一个方法是 test(),接收一个字符串参数。如果输入的文本与模式匹配,则返回 true,否则返回 false,常用于条件判断:
let text = "000-00-0000";
let pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)) {
console.log("The pattern was matched.");
}
RegExp 构造函数本身也有几个属性,这些属性适用于作用域中的所有正则表达式,而且会根据最后执行的正则表达式操作而变化,可以通过全名或者简写进行访问
全名 | 简写 | 说明 |
---|---|---|
input | $_ | 最后搜索的字符串 |
lastMatch | $& | 最后匹配的文本 |
lastParen | $+ | 最后匹配的捕获组 |
leftContext | $` | input字符串中出现在lastMatch前面的文本 |
rightContext | $' | input字符串中出现在lastMatch后面的文本 |
let text = "this has been a short summer";
let pattern = /(.)hort/g;
if (pattern.test(text)) {
console.log(RegExp.input); // this has been a short summer
console.log(RegExp.leftContext); // this has been a
console.log(RegExp.rightContext); // summer
console.log(RegExp.lastMatch); // short
console.log(RegExp.lastParen); // s
}
以上属性名也可以替换成简写形式
let text = "this has been a short summer";
let pattern = /(.)hort/g;
if (pattern.test(text)) {
console.log(RegExp.$_); // this has been a short summer
console.log(RegExp["$`"]); // this has been a
console.log(RegExp["$'"]); // summer
console.log(RegExp["$&"]); // short
console.log(RegExp["$+"]); // s
}
RegExp 还有其他几个构造函数属性,可以存储最多 9 个捕获组的匹配项,这些属性通过 RegExp.$1~RegExp.$9 来访问
let text = "this has been a short summer";
let pattern = /(..)or(.)/g;
if (pattern.test(text)) {
console.log(RegExp.$1); // sh
console.log(RegExp.$2); // t
}